package com.nifostasky.distributed.initredis;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;


/**
 * redis 原生api实现分布式锁操作
 */
@Slf4j
@RestController
@RequestMapping("/redis")
public class RedisLockController {

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    // 商品key
    private static final String KEY = "book";
    // 库存数量
    private static final  Long STOCK = 50L;

    // 锁标识
    private static final String LOCK_KEY = "book_lock";

    /**
     * 初始化
     */
    @GetMapping("/init")
    public String init() {
        stringRedisTemplate.opsForValue().set(KEY, String.valueOf(STOCK));
        return "初始化成功～";
    }

    /**
     * 购买图书
     * 未在获取 book 这个key操作的时候加锁;
     * 会出现 库存数量小于0 的情况 :这种状况被称为 超卖
     */
    @GetMapping("/buy")
    public String buy() {
        // 获取到当前库存
        String buyBefore = stringRedisTemplate.opsForValue().get(KEY);
        if(Objects.isNull(buyBefore)) {
            log.error("未找到\"{}\"的库存信息~", KEY);
            return "暂未上架～";
        }
        long buyBeforeL = Long.parseLong(buyBefore);
        if(buyBeforeL > 0) {
            // 对库存进行-1操作
            Long buyAfter = stringRedisTemplate.opsForValue().decrement(KEY);
            log.info("剩余图书==={}", buyAfter);
            return "购买成功～";
        }
        else {
            log.info("库存不足～");
            return "库存不足～";
        }
    }

    /**
     * 购买图书
     */
    @GetMapping("/buy1")
    public String buy1() {
        String clientId = UUID.randomUUID().toString();
        /*
         * 给redis设置一个key，并设置过期时间防止死锁
         * setIfAbsent（setnx）：当key不存在时才设置值
         * flag=true：值设置成功（获取锁） flag=false：设置值失败（获取锁失败）
         */
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(LOCK_KEY, clientId, 30, TimeUnit.SECONDS);
        try {
            if(Objects.nonNull(flag) && flag) {
                String buyBefore = stringRedisTemplate.opsForValue().get(KEY);
                if(Objects.isNull(buyBefore)) {
                    log.error("未找到\"{}\"的库存信息~", KEY);
                    return "暂未上架～";
                }
                long buyBeforeL = Long.parseLong(buyBefore);
                if(buyBeforeL > 0) {
                    Long buyAfter = stringRedisTemplate.opsForValue().decrement(KEY);
                    log.info("剩余图书==={}", buyAfter);
                    return "购买成功～";
                }
                else {
                    log.info("库存不足～");
                    return "库存不足～";
                }
            }
            else {
                log.error("获取锁失败～");
            }
        }
        catch(Exception e) {
            e.printStackTrace();
        }
        finally {
            // 防止误删锁
            if(clientId.equals(stringRedisTemplate.opsForValue().get(LOCK_KEY))) {
                stringRedisTemplate.delete(LOCK_KEY);
            }
        }
        return "系统错误～";
    }
}
